﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using DDotNet.ORM.DataFactory.DirectAccess.Enum;
using DDotNet.ORM.DataFactory.Exception;
using DDotNet.ORM.DataFactory.Helper;
using DDotNet.ORM.DataFactory.Interface;
using DDotNet.ORM.DataFactory.DirectAccess.QueryParameter;
using DDotNet.ORM.DataFactory.DirectAccess.SortParameter;
using DDotNet.ORM.ORMapping.Attribute;
using DDotNet.ORM.ORMapping.Entity;
using DDotNet.ORM.ORMapping.Exception;
using DDotNet.ORM.ORMapping.Interface;
using DDotNet.ORM.ORMapping.OREntity;

namespace DDotNet.ORM.DataFactory.DirectAccess
{
    /// <summary>
    /// Direct Access Entity Query Factory
    /// </summary>
    public sealed class DirectAccessQueryFactory
    {
        #region Constructor

        private DirectAccessQueryFactory()
        {

        }

        public DirectAccessQueryFactory(ICrudFactory crudFactory)
        {
            this._CRUDFactory = crudFactory;
        }

        #endregion

        #region Fields

        private ICrudFactory _CRUDFactory;

        #endregion

        #region Métodos de Seleção de Coleção

        /// <summary>
        /// Seleciona Todos os Registros de uma Determinada Tabela do Banco de Dados
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectAll(Type mappedEntityType)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(),
                                    new GenericSortParameterCollection()));
        }

        /// <summary>
        /// Seleciona Todos os Registros de uma Determinada Tabela do Banco de Dados
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="genericSortParameter">Indica um Campo para Ordenação do Resultados</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectAll(Type mappedEntityType,
                                                                    GenericSortParameter genericSortParameter)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(),
                                    new GenericSortParameterCollection(genericSortParameter)));
        }

        /// <summary>
        /// Seleciona Todos os Registros de uma Determinada Tabela do Banco de Dados
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="genericSortParameters">Campos para a Ordenação do Resultados</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectAll(Type mappedEntityType,
                                                                    GenericSortParameterCollection genericSortParameters)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(),
                                    genericSortParameters));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Único Parâmetro a Serem Utilizado na Busca</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameter parameterA)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(parameterA),
                                    new GenericSortParameterCollection()));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Único Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="genericSortParameter">Indica um Campo para Ordenação do Resultados</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameter parameterA,
                                                                        GenericSortParameter genericSortParameter)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(parameterA),
                                    new GenericSortParameterCollection(genericSortParameter)));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Único Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="genericSortParameters">Campos para a Ordenação do Resultados</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameter parameterA,
                                                                        GenericSortParameterCollection
                                                                            genericSortParameters)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(parameterA),
                                    genericSortParameters));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Primeiro Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterB">Segundo Parâmetro a Serem Utilizado na Busca</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameter parameterA,
                                                                        BaseParameter parameterB)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(parameterA, parameterB),
                                    new GenericSortParameterCollection()));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Primeiro Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterB">Segundo Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="genericSortParameter">Indica um Campo para Ordenação do Resultados</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameter parameterA,
                                                                        BaseParameter parameterB,
                                                                        GenericSortParameter genericSortParameter)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(parameterA, parameterB),
                                    new GenericSortParameterCollection(genericSortParameter)));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Primeiro Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterB">Segundo Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="genericSortParameters">Campos para a Ordenação do Resultados</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameter parameterA,
                                                                        BaseParameter parameterB,
                                                                        GenericSortParameterCollection
                                                                            genericSortParameters)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(parameterA, parameterB),
                                    genericSortParameters));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Primeiro Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterB">Segundo Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterC">Terceiro Parâmetro a Serem Utilizado na Busca</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameter parameterA,
                                                                        BaseParameter parameterB,
                                                                        BaseParameter parameterC)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(parameterA, parameterB, parameterC),
                                    new GenericSortParameterCollection()));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Primeiro Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterB">Segundo Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterC">Terceiro Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="genericSortParameter">Indica um Campo para Ordenação do Resultados</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameter parameterA,
                                                                        BaseParameter parameterB,
                                                                        BaseParameter parameterC,
                                                                        GenericSortParameter genericSortParameter)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(parameterA, parameterB, parameterC),
                                    new GenericSortParameterCollection(genericSortParameter)));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Primeiro Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterB">Segundo Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterC">Terceiro Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="genericSortParameters">Campos para a Ordenação do Resultados</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameter parameterA,
                                                                        BaseParameter parameterB,
                                                                        BaseParameter parameterC,
                                                                        GenericSortParameterCollection
                                                                            genericSortParameters)
        {
            return
                (this.SelectByQuery(mappedEntityType, new BaseParameterCollection(parameterA, parameterB, parameterC),
                                    genericSortParameters));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameters">Parâmetros a Serem Utilizados na Busca</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameterCollection parameters)
        {
            return
                (this.SelectByQuery(mappedEntityType, parameters,
                                    new GenericSortParameterCollection()));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameters">Parâmetros a Serem Utilizados na Busca</param>
        /// <param name="genericSortParameter">Indica um Campo para Ordenação do Resultados</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameterCollection parameters,
                                                                        GenericSortParameter genericSortParameter)
        {
            return
                (this.SelectByQuery(mappedEntityType, parameters,
                                    new GenericSortParameterCollection(genericSortParameter)));
        }

        /// <summary>
        /// Seleciona um Conjunto de Itens com Base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameters">Parâmetros a Serem Utilizados na Busca</param>
        /// <param name="genericSortParameters">Campos para a Ordenação do Resultados</param>
        /// <returns></returns>
        public BusinessCollectionBase<BusinessEntityBase> SelectByQuery(Type mappedEntityType,
                                                                        BaseParameterCollection parameters,
                                                                        GenericSortParameterCollection
                                                                            genericSortParameters)
        {
            // Create Objects
            CRUDHelper crudHelper = ((IHelpeableCRUDFactory) this._CRUDFactory).GetCRUDHelper();
            IDbCommand dbCommand = crudHelper.GetDataBaseFactory(MappedEntityHelper.Instance.GetConnectionStringKey(mappedEntityType)).
                    GetNewCommand();

            // Get ODBC TextCommand
            dbCommand.CommandText =
                this.GetCustomSelectCommand(mappedEntityType, parameters, genericSortParameters, false,
                                            crudHelper.GetDataBaseFactory(
                                                MappedEntityHelper.Instance.GetConnectionStringKey(mappedEntityType)));

            // Setup Generic ODBC Command Settings
            dbCommand.CommandType = CommandType.Text;

            // Add Query Parameters
            foreach (BaseParameter parameter in parameters)
            {
                foreach (
                    IDbDataParameter dbDataParameter in
                        parameter.GetParameters(mappedEntityType,
                                                crudHelper.GetDataBaseFactory(
                                                    MappedEntityHelper.Instance.GetConnectionStringKey(mappedEntityType)))
                    )
                {
                    dbCommand.Parameters.Add(dbDataParameter);
                }
            }

            // Execute Reader and Extrace Collection Data And Return
            try
            {
                // Execute Query
                return ((IHelpeableCRUDFactory) this._CRUDFactory).ExtractCollection(dbCommand.ExecuteReader(), mappedEntityType);
            }
            catch (System.Exception ex)
            {
                // Rollback Transaction
                crudHelper.RollbackTransaction();

                // Throw Exception
                throw (new DataBaseException("Ocorreu um erro ao realizar uma consulta", mappedEntityType, ex));
            }
        }

        #endregion

        #region Método de Seleção de Item

        /// <summary>
        /// Seleciona um Item Individual com base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Único Parâmetro a Serem Utilizado na Busca</param>
        /// <returns></returns>
        public BusinessEntityBase SelectItemByQuery(Type mappedEntityType, BaseParameter parameterA)
        {
            return (this.SelectItemByQuery(mappedEntityType, new BaseParameterCollection(parameterA)));
        }

        /// <summary>
        /// Seleciona um Item Individual com base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Primeiro Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterB">Segundo Parâmetro a Serem Utilizado na Busca</param>
        /// <returns></returns>
        public BusinessEntityBase SelectItemByQuery(Type mappedEntityType, BaseParameter parameterA,
                                                    BaseParameter parameterB)
        {
            return (this.SelectItemByQuery(mappedEntityType, new BaseParameterCollection(parameterA, parameterB)));
        }

        /// <summary>
        /// Seleciona um Item Individual com base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameterA">Primeiro Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterB">Segundo Parâmetro a Serem Utilizado na Busca</param>
        /// <param name="parameterC">Terceiro Parâmetro a Serem Utilizado na Busca</param>
        /// <returns></returns>
        public BusinessEntityBase SelectItemByQuery(Type mappedEntityType, BaseParameter parameterA,
                                                    BaseParameter parameterB, BaseParameter parameterC)
        {
            return
                (this.SelectItemByQuery(mappedEntityType,
                                        new BaseParameterCollection(parameterA, parameterB, parameterC)));
        }

        /// <summary>
        /// Seleciona um Item Individual com base nos Parâmetros para a Busca
        /// </summary>
        /// <param name="mappedEntityType">Tipo da Entidade Representante da Tabela do Banco de Dados</param>
        /// <param name="parameters">Parâmetros a Serem Utilizados na Busca</param>
        /// <returns></returns>
        public BusinessEntityBase SelectItemByQuery(Type mappedEntityType,
                                                    BaseParameterCollection parameters)
        {
            // Create Objects
            BusinessCollectionBase<BusinessEntityBase> result;

            // Get Result
            result = this.SelectByQuery(mappedEntityType, parameters);

            // Return
            if (result.Count > 0)
            {
                // Limpa o Pai da Entidade
                result[0].ClearParent();

                // Retorna
                return result[0];
            }
            else
            {
                // Retorna Nulo
                return null;
            }
        }

        #endregion

        #region Métodos Internos

        /// <summary>
        /// Get a List With Entity DB Fields List to SELECT Command
        /// </summary>
        /// <param name="mappedEntityType"></param>
        /// <returns></returns>
        private string GetEntityDBFieldsList(Type mappedEntityType)
        {
            // Create Objects
            StringBuilder fieldsBuilder;
            List<ExtendedPropertyInfo> properties;
            bool useSeparator = false;

            // Create Cache Objects
            string cacheKey = "CustomDBFieldsList" + mappedEntityType;

            // Check Cache To Get a Cached Script
            if (CacheManager.Instance.CheckIfContains(cacheKey))
            {
                return (string)CacheManager.Instance.GetFromCache(cacheKey);
            }

            // Get Entity Properties
            properties = MappedEntityHelper.Instance.GetAvailableProperties(mappedEntityType, this._CRUDFactory);

            // Initialize a "Fields Builder"
            fieldsBuilder = new StringBuilder();

            // Looping on Properties to Generate a Fields List
            foreach (ExtendedPropertyInfo info in properties)
            {
                // Check If Property Have a PropertyMapping and a DBFieldName
                if (info.PropertyMapping != null && !string.IsNullOrEmpty(info.PropertyMapping.DBFieldName))
                {
                    // Check If Need to use a Separator
                    if (useSeparator)
                    {
                        fieldsBuilder.Append(", ");
                    }
                    else
                    {
                        useSeparator = true;
                    }
                    fieldsBuilder.Append(info.PropertyMapping.DBFieldName);
                    fieldsBuilder.Append(" as [");
                    fieldsBuilder.Append(info.PropertyMapping.DBFieldName);
                    fieldsBuilder.Append("]");
                }
            }

            // Add To Cache
            CacheManager.Instance.AddToCache(cacheKey, fieldsBuilder.ToString());

            // Return Result
            return fieldsBuilder.ToString();
        }

        private PropertyMapping GetFirstAvailableMapping(Type mappedEntityType)
        {
            // Create Objects
            List<ExtendedPropertyInfo> properties;

            // Get Entity Properties
            properties = MappedEntityHelper.Instance.GetAvailableProperties(mappedEntityType, this._CRUDFactory);

            // Looping on Properties to Generate a Fields List
            foreach (ExtendedPropertyInfo info in properties)
            {
                // Check If Property Have a PropertyMapping and a DBFieldName
                if (info.PropertyMapping != null && !string.IsNullOrEmpty(info.PropertyMapping.DBFieldName))
                {
                    return info.PropertyMapping;
                }
            }

            // Return Null
            return null;
        }


        private string GetCustomSelectCommand(Type mappedEntityType, BaseParameterCollection parameters,
                                              GenericSortParameterCollection genericSortParameters,
                                              bool addCountModifier, IDataBaseFactory dataBaseFactory)
        {
            // Create Objects
            StringBuilder queryBuilder = new StringBuilder();
            bool useSeparator = false;

            // Create Base Query
            if (addCountModifier)
            {
                queryBuilder.AppendFormat("SELECT COUNT({0}) FROM {1}",
                                          GetFirstAvailableMapping(mappedEntityType).DBFieldName,
                                          MappedEntityHelper.Instance.GetTableName(mappedEntityType));
            }
            else
            {
                queryBuilder.AppendFormat("SELECT {0} FROM {1}",
                                          GetEntityDBFieldsList(mappedEntityType),
                                          MappedEntityHelper.Instance.GetTableName(mappedEntityType));
            }

            // Get Where Clausule
            if (parameters != null && parameters.Count > 0)
            {
                // Add Base Where Clausule
                queryBuilder.Append(" WHERE (");

                // Looping on Itens
                foreach (BaseParameter parameter in parameters)
                {
                    // Check If Need to use a Separator
                    if (useSeparator)
                    {
                        queryBuilder.Append(" AND ");
                    }
                    else
                    {
                        useSeparator = true;
                    }

                    // Append a QueryString
                    queryBuilder.AppendFormat("({0})", parameter.GetQueryString(mappedEntityType, dataBaseFactory));
                }

                // Close Base Where Clausule
                queryBuilder.Append(")");
            }

            // Get ORDER BY Clausules
            useSeparator = false;
            if (genericSortParameters != null && genericSortParameters.Count > 0)
            {
                // Add Base Where Clausule
                queryBuilder.Append(" ORDER BY ");

                // Looping on Itens
                foreach (GenericSortParameter sortParameter in genericSortParameters)
                {
                    // Check If Need to use a Separator
                    if (useSeparator)
                    {
                        queryBuilder.Append(" , ");
                    }
                    else
                    {
                        useSeparator = true;
                    }

                    // Append a QueryString
                    queryBuilder.AppendFormat("{0}", sortParameter.GetQueryString(mappedEntityType));
                }
            }


            // Return Data
            return queryBuilder.ToString();
        }




        /// <summary>
        /// Carrega um Link
        /// </summary>
        /// <param name="mappedEntity">Entidade Alvo</param>
        /// <param name="targetChildrenProperty">Propriedade Alvo</param>
        internal void SelectLink(BusinessEntityBase mappedEntity, ExtendedPropertyInfo targetChildrenProperty)
        {
            // Cria Objetos
            BusinessCollectionBase<BusinessEntityBase> result;
            BaseParameterCollection queryParameters = new BaseParameterCollection();
            object targetProperty = Activator.CreateInstance(targetChildrenProperty.PropertyType.PropertyType);
            string targetPropetyName;

            // Looping nos Parâmetros das FK's
            foreach (string parameter in targetChildrenProperty.ChildrenPropertyMapping.ChildrenEntityPropertyName)
            {
                targetPropetyName =
                    MappedEntityHelper.Instance.GetPropertyNameFromLinkItem(mappedEntity, targetChildrenProperty,
                                                                            parameter, this._CRUDFactory);

                // Verifica se Achou
                if (string.IsNullOrEmpty(targetPropetyName))
                {
                    throw (new OperationException(
                        "Não foi possível encontrar as referências de mapeamento para a entidade \"" +
                        mappedEntity.GetType() +
                        "\". Verifique se todas as propriedades de ligação (FK) nas propriedades filhas estão corretamente mapeadas"));
                }

                // Para Cada FK, procura qual a Propriedade da Entidade Pai que Representa a Ligação
                queryParameters.Add(new GenericQueryParameter(parameter,
                                                              GenericComparerMode.Equals,
                                                              mappedEntity[targetPropetyName]));
            }

            // Verifica se a Entidade Alvo (Link) é uma Coleção ou Entidade Simples
            if (MappedEntityHelper.Instance.IsPropertySingleEntity(targetChildrenProperty)) // Entidade Simples
            {
                // Exec a Query
                targetProperty =
                    this.SelectItemByQuery(
                        targetChildrenProperty.PropertyType.PropertyType,
                        queryParameters);
            }
            else // Coleção
            {
                // Exec a Query
                result =
                    this.SelectByQuery(
                        ((IBusinessCollectionState)targetProperty).GetContentInstance().GetType(),
                        queryParameters);

                // Fill Target Collection With Result
                targetProperty.GetType().GetMethod("Fill").Invoke(targetProperty, new object[] { result });
            }

            // Set List Value into Target Entity
            targetChildrenProperty.PropertyType.SetValue(mappedEntity, targetProperty, null);
        }

        #endregion

        #region Métodos de Contagem de Registro

        /// <summary>
        /// Realiza uma Contagem dos Registros de uma Tabela
        /// </summary>
        /// <returns>Quantidade de Registros Contados</returns>
        public long SelectCount(Type mappedEntityType)
        {
            return (this.SelectCount(mappedEntityType, new BaseParameterCollection()));
        }

        /// <summary>
        /// Realiza uma Contagem dos Registros de uma Tabela
        /// </summary>
        /// <param name="parameterA">Primeiro Parâmetro a Ser Utilizado na Busca</param>
        /// <returns>Quantidade de Registros Contados</returns>
        public long SelectCount(Type mappedEntityType, BaseParameter parameterA)
        {
            return (this.SelectCount(mappedEntityType, new BaseParameterCollection(parameterA)));
        }

        /// <summary>
        /// Realiza uma Contagem dos Registros de uma Tabela
        /// </summary>
        /// <param name="parameterA">Primeiro Parâmetro a Ser Utilizado na Busca</param>
        /// <param name="parameterB">Segundo Parâmetro a Ser Utilizado na Busca</param>
        /// <returns>Quantidade de Registros Contados</returns>
        public long SelectCount(Type mappedEntityType, BaseParameter parameterA, BaseParameter parameterB)
        {
            return (this.SelectCount(mappedEntityType, new BaseParameterCollection(parameterA, parameterB)));
        }

        /// <summary>
        /// Realiza uma Contagem dos Registros de uma Tabela
        /// </summary>
        /// <param name="parameterA">Primeiro Parâmetro a Ser Utilizado na Busca</param>
        /// <param name="parameterB">Segundo Parâmetro a Ser Utilizado na Busca</param>
        /// <param name="parameterC">Terceiro Parâmetro a Ser Utilizado na Busca</param>
        /// <returns>Quantidade de Registros Contados</returns>
        public long SelectCount(Type mappedEntityType, BaseParameter parameterA, BaseParameter parameterB,
                                BaseParameter parameterC)
        {
            return
                (this.SelectCount(mappedEntityType, new BaseParameterCollection(parameterA, parameterB, parameterC)));
        }

        /// <summary>
        /// Realiza uma Contagem dos Registros de uma Tabela
        /// </summary>
        /// <param name="parameters">Parâmetros a Serem Utilizados</param>
        /// <returns>Quantidade de Registros Contados</returns>
        public long SelectCount(Type mappedEntityType, BaseParameterCollection parameters)
        {
            // Create Objects
            CRUDHelper crudHelper = ((IHelpeableCRUDFactory)this._CRUDFactory).GetCRUDHelper();
            IDbCommand dbCommand =
                crudHelper.GetDataBaseFactory(MappedEntityHelper.Instance.GetConnectionStringKey(mappedEntityType)).
                    GetNewCommand();

            // Get ODBC TextCommand
            dbCommand.CommandText =
                this.GetCustomSelectCommand(mappedEntityType, parameters, null, true,
                                            crudHelper.GetDataBaseFactory(
                                                MappedEntityHelper.Instance.GetConnectionStringKey(mappedEntityType)));

            // Setup Generic ODBC Command Settings
            dbCommand.CommandType = CommandType.Text;

            // Add Query Parameters
            foreach (BaseParameter parameter in parameters)
            {
                foreach (
                    IDbDataParameter dbDataParameter in
                        parameter.GetParameters(mappedEntityType,
                                                crudHelper.GetDataBaseFactory(
                                                    MappedEntityHelper.Instance.GetConnectionStringKey(mappedEntityType)))
                    )
                {
                    dbCommand.Parameters.Add(dbDataParameter);
                }
            }

            // Execute Reader and Extrace Collection Data And Return
            try
            {
                // Execute Query
                return (long)Convert.ChangeType(dbCommand.ExecuteScalar(), typeof(long));
            }
            catch (System.Exception ex)
            {
                // Rollback Transaction
                crudHelper.RollbackTransaction();

                // Throw Exception
                throw (new DataBaseException("Ocorreu um erro ao realizar uma contagem de registros", mappedEntityType, ex));
            }
        }

        #endregion

        #region Métodos de Seleção de Entidades Filhas

        /// <summary>
        /// Carrega uma Entidade Filha (Link) a partir de uma Entidade Pai
        /// </summary>
        /// <param name="targetMappedEntity">Entidade Alvo da Carga do Link</param>
        /// <param name="childrenPropertyName">Nome da Propriedade de Link a ser Carregada</param>
        public void SelectChildren(BusinessEntityBase targetMappedEntity, string childrenPropertyName)
        {
            // Create Properties
            List<ExtendedPropertyInfo> childrenProperties;

            // Get a Property
            childrenProperties = MappedEntityHelper.Instance.GetChildrenProperties(targetMappedEntity, this._CRUDFactory);

            // Looping on Properties
            foreach (ExtendedPropertyInfo childrenProperty in childrenProperties)
            {
                // Check if Children Property are a Correct Property
                if (childrenProperty.PropertyType.Name.ToUpper().Equals(childrenPropertyName.ToUpper()))
                {
                    this.SelectLink(targetMappedEntity, childrenProperty);
                }
            }
        }

        /// <summary>
        /// Carrega Todas as Entidades Filhas (Links) da Entidade Pai
        /// </summary>
        /// <param name="targetMappedEntity">Entidade Alvo da Carga do Link</param>
        public void SelectAllChildrens(BusinessEntityBase targetMappedEntity)
        {
            // Create Objects
            List<ExtendedPropertyInfo> childrenProperties;

            // Get Children Properties
            childrenProperties = MappedEntityHelper.Instance.GetChildrenProperties(targetMappedEntity, this._CRUDFactory);

            // Looping on Properties
            foreach (ExtendedPropertyInfo childrenProperty in childrenProperties)
            {
                this.SelectChildren(targetMappedEntity, childrenProperty.PropertyType.Name);
            }
        }

        #endregion
    }
}
